package com.spider.moudle.api.realize;

import com.google.common.collect.Lists;
import com.spider.moudle.api.Downloader;
import com.spider.moudle.constant.Constant;
import com.spider.moudle.entity.Proxy;
import com.spider.moudle.entity.ResponseEntity;
import com.spider.moudle.factory.DownloaderFactory;
import com.spider.moudle.factory.HttpClientFactory;
import com.spider.moudle.proxy.HttpProxyContext;
import com.spider.moudle.proxy.HttpsProxyContext;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 夏茂轩
 * @description 下载工具
 * @date 2017/12/8 9:22
 */
public class StandardDownloader implements Downloader {
    private List<Header> headers = Lists.newArrayList(new BasicHeader(HttpHeaders.CONNECTION, "close"));
    private String encoding = Constant.DEFUALT_ENCODING;
    private Collection<Header> defaultHeaders = Lists.newArrayList(new BasicHeader("User-Agent", Constant.HTTP_USER_AGENT)
            , new BasicHeader("Accept-Encoding", Constant.ACCEPT_ENCODING));
    private StandardDownloader() {
    }

    private StandardDownloader(String encoding) {
        this.encoding = encoding;
    }

    public void setHeaders(List<Header> headers) {
        this.headers = headers;
    }

    public StandardDownloader(List<Header> headers, String encoding) {
        this.headers = headers;
        this.encoding = encoding;
    }

    @Override
    public ResponseEntity get(String url) throws Exception {
        ResponseEntity responseEntity = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClientFactory.buildHttpClient(false);
            HttpUriRequest request = DownloaderFactory.buildRequest(url, "get", null, HttpClientFactory.buildRequestConfig(null),this.headers,this.encoding);
            responseEntity = DownloaderFactory.execute(request, httpClient, null,this.encoding);
        } catch (IOException e) {
            throw e;
        }finally {
            DownloaderFactory.handClient(httpClient);
        }
        return responseEntity;
    }

    @Override
    public ResponseEntity post(String url) throws Exception {
        ResponseEntity responseEntity = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClientFactory.buildHttpClient(false);
            HttpUriRequest request = DownloaderFactory.buildRequest(url, "post", null, HttpClientFactory.buildRequestConfig(null),this.headers,this.encoding);
            responseEntity = DownloaderFactory.execute(request, httpClient, null,this.encoding);
        } catch (Exception e) {
            throw e;
        }finally {
            DownloaderFactory.handClient(httpClient);
        }
        return responseEntity;
    }

    @Override
    public ResponseEntity get(String url, boolean useProxy) throws Exception {
        if (!useProxy) {
            return get(url);
        } else {
            Proxy proxy = null;
            ResponseEntity responseEntity = null;
            CloseableHttpClient httpClient = null;
            //添加Header
            int count = 0;
            while (true) {
                if (DownloaderFactory.isHttps(url)) {
                    proxy = HttpsProxyContext.getInstance().fetchProxy();
                } else {
                    proxy = HttpProxyContext.getInstance().fetchProxy();
                }
                try {
                    httpClient = HttpClientFactory.buildHttpClient(true);
                    HttpUriRequest request = DownloaderFactory.buildRequest(url, "get", null, HttpClientFactory.buildRequestConfig(new HttpHost(proxy.getIp(), proxy.getPort())),this.headers,this.encoding);
                    responseEntity = DownloaderFactory.execute(request, httpClient, proxy,this.encoding);
                    if(responseEntity.getStatus_code()!=200){
                        continue;
                    }
                    return responseEntity;
                } catch (Exception e) {
                    if (handException(e, count)) {
                        continue;
                    } else {
                        throw e;
                    }
                }finally {
                    count++;
                }
            }
        }
    }

    @Override
    public ResponseEntity post(String url, boolean useProxy) throws Exception {
        if (!useProxy) {
            return post(url);
        } else {
            Proxy proxy = null;
            ResponseEntity responseEntity = null;
            CloseableHttpClient httpClient = null;
            //添加Header
            int count = 0;
            while (true) {
                if (DownloaderFactory.isHttps(url)) {
                    proxy = HttpsProxyContext.getInstance().fetchProxy();
                } else {
                    proxy = HttpProxyContext.getInstance().fetchProxy();
                }
                try {
                    httpClient = HttpClientFactory.buildHttpClient(true);
                    HttpUriRequest request = DownloaderFactory.buildRequest(url, "post", null, HttpClientFactory.buildRequestConfig(new HttpHost(proxy.getIp(), proxy.getPort())),this.headers,this.encoding);
                    responseEntity = DownloaderFactory.execute(request, httpClient, proxy,this.encoding);
                    if(responseEntity.getStatus_code()!=200){
                        continue;
                    }
                    return responseEntity;
                } catch (Exception e) {
                    if (handException(e, count)) {
                        continue;
                    } else {
                        throw e;
                    }
                }finally {
                    count++;
                }
            }
        }
    }

    @Override
    public void addHeader(String name, String val) {
        synchronized (headers) {
            BasicHeader header = new BasicHeader(name, val);
            headers.add(header);
            headers.notifyAll();
        }
    }

    @Override
    public ResponseEntity get(String url, Proxy proxy) throws Exception {
        ResponseEntity responseEntity = null;
        CloseableHttpClient httpClient = null;
        httpClient = HttpClientFactory.buildHttpClient(true);
        HttpUriRequest request = DownloaderFactory.buildRequest(url, "get", null, HttpClientFactory.buildRequestConfig(new HttpHost(proxy.getIp(), proxy.getPort())),this.headers,this.encoding);
        responseEntity = DownloaderFactory.execute(request, httpClient, proxy,this.encoding);
        return responseEntity;
    }

    @Override
    public ResponseEntity download(String url) throws Exception {
        ResponseEntity responseEntity = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClientFactory.buildHttpClient(false);
            HttpUriRequest request = DownloaderFactory.buildRequest(url, "get", null, HttpClientFactory.buildRequestConfig(null),this.headers,this.encoding);
            responseEntity = DownloaderFactory.executeSource(request, httpClient, null,this.encoding);
        } catch (IOException e) {
            throw e;
        }finally {
            DownloaderFactory.handClient(httpClient);
        }
        return responseEntity;
    }

    @Override
    public ResponseEntity post(String url, Map<String, String> params) throws Exception {
        ResponseEntity responseEntity = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClientFactory.buildHttpClient(false);
            HttpUriRequest request = DownloaderFactory.buildRequest(url, "post", params, HttpClientFactory.buildRequestConfig(null),this.headers,this.encoding);
            responseEntity = DownloaderFactory.execute(request, httpClient, null,this.encoding);
        } catch (IOException e) {
            throw e;
        }finally {
            DownloaderFactory.handClient(httpClient);
        }
        return responseEntity;
    }

    @Override
    public ResponseEntity post(String url, Map<String, String> params, int timeOut) throws Exception {
        ResponseEntity responseEntity = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClientFactory.buildHttpClient(false);
            HttpUriRequest request = DownloaderFactory.buildRequest(url, "post", params, HttpClientFactory.buildRequestConfig(null,timeOut),this.headers,this.encoding);
            responseEntity = DownloaderFactory.execute(request, httpClient, null,this.encoding);
        } catch (IOException e) {
            throw e;
        }finally {
            DownloaderFactory.handClient(httpClient);
        }
        return responseEntity;
    }

    @Override
    public ResponseEntity post(String url, Map<String, String> params, boolean useProxy) throws Exception {
        if (!useProxy) {
            return post(url, params);
        } else {
            ResponseEntity responseEntity = null;
            Proxy proxy = null;
            int count = 0;
            CloseableHttpClient httpClient = null;
            while (true) {
                if (DownloaderFactory.isHttps(url)) {
                    proxy = HttpsProxyContext.getInstance().fetchProxy();
                } else {
                    proxy = HttpProxyContext.getInstance().fetchProxy();
                }
                try {
                    httpClient = HttpClientFactory.buildHttpClient(true);
                    HttpUriRequest request = DownloaderFactory.buildRequest(url, "post", params, HttpClientFactory.buildRequestConfig(new HttpHost(proxy.getIp(), proxy.getPort())),this.headers,this.encoding);
                    responseEntity = DownloaderFactory.execute(request, httpClient, proxy,this.encoding);
                    if(responseEntity.getStatus_code()!=200){
                        continue;
                    }
                    return responseEntity;
                } catch (Exception e) {
                    if (handException(e, count)) {
                        continue;
                    } else {
                        throw e;
                    }
                } finally {
                    count++;
                }
            }
        }
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    private boolean handException(Exception e, int count) {
        if (e instanceof ConnectTimeoutException && count < Constant.HTTP_PROXY_SWITCH_TIMES) {
            return true;
        } else if (e instanceof SocketTimeoutException && count < Constant.HTTP_PROXY_SWITCH_TIMES) {
            return true;
        } else if (e instanceof SocketException && count < Constant.HTTP_PROXY_SWITCH_TIMES) {
            return true;
        } else {
            if(count < Constant.HTTP_PROXY_SWITCH_TIMES){
                return true;
            }else {
                return false;
            }
        }
    }
}
